import {
  FieldNames,
  InternalDataNodeForDataMap,
  InternalFlattenNode,
  Key,
  YmDataNode,
} from '../interface';
import { MOTION_KEY } from '../NodeList';

export function fillFieldNames(
  fieldNames: FieldNames | undefined,
): Required<FieldNames> {
  const { title, key, children } = fieldNames || {};

  return {
    title: title || 'title',
    key: key || 'key',
    children: children || 'children',
  };
}

/**
 *
 *
 * @export
 * @param {YmDataNode[]} treeData
 * @param {FieldNames} [fieldNames]
 * @description 把treeData 转换成以key/pos 为key的对象,
 * @description 把多维数组转换成链表数据结构
 * @returns {{keyMap:Record<string,InternalDataNodeForDataMap>,posMap:Record<string,InternalDataNodeForDataMap>}}
 */
export function converTreeDataToMap(
  treeData: YmDataNode[],
  fieldNames: FieldNames | undefined,
): {
  keyMap: Record<string, InternalDataNodeForDataMap>;
  posMap: Record<string, InternalDataNodeForDataMap>;
} {
  const currFieldNames = fillFieldNames(fieldNames);
  const keyMap: Record<string, InternalDataNodeForDataMap> = {};
  const posMap: Record<string, InternalDataNodeForDataMap> = {};

  function process(
    dataList: YmDataNode[],
    parentPos: string,
    parentLevel: number,
    pathNodes: YmDataNode[],
  ) {
    for (let i = 0; i < dataList.length; i++) {
      const currDataNode = dataList[i];
      const currPathNodes = [...pathNodes, currDataNode];
      const currPos = `${parentPos}-${i}`;
      const currLevel = parentLevel + 1;
      const currKey = currDataNode[currFieldNames.key];
      const currChildren = currDataNode[currFieldNames.children];
      const data: InternalDataNodeForDataMap = {
        key: currKey,
        pos: currPos,
        level: currLevel,
        index: i,
        node: currDataNode,
        nodes: currPathNodes,
      };
      const parent = posMap[parentPos];
      if (parent) {
        parent.children = parent.children ?? [];
        parent.children.push(data); //父级的children属性由子级献祭
        data.parent = parent;
      }
      keyMap[currKey] = data;
      posMap[currPos] = data;

      if (currChildren) {
        process(currChildren, currPos, currLevel, currPathNodes);
      }
    }
  }

  process(treeData, '0', -1, []);

  return {
    keyMap,
    posMap,
  };
}

/**
 *
 *
 * @export
 * @param {Key[]} expandedKeys
 * @param {Record<string,InternalDataNodeForDataMap>} keyMap
 * @description  defaultExpandedKeys/expandedKeys找到他们所有的祖先keys
 * @returns {Key[]}
 */
export function conductExpandParent(
  expandedKeys: Key[],
  keyMap: Record<string, InternalDataNodeForDataMap>,
): Key[] {
  const keySet = new Set<Key>();

  function getKey(key: Key) {
    if (!keySet.has(key)) {
      const { parent, node } = keyMap[key];
      if (!node.disabled) {
        //这个要验证
        keySet.add(key);
        if (parent) {
          getKey(parent.key);
        }
      }
    }
  }
  expandedKeys.forEach((key) => {
    getKey(key);
  });

  return [...keySet];
}

/**
 *
 *
 * @export
 * @param {YmDataNode[]} treeData
 * @param {FieldNames} fieldNames
 * @param {Key[]} expandedKeys
 * @description 根据expendKeys获取需要渲染的treeData，并打平
 */
export function flattenTreeDataByExpendKeys(
  treeData: YmDataNode[],
  fieldNames: FieldNames | undefined,
  expandedKeys: Key[],
): InternalFlattenNode[] {
  const currFieldNames = fillFieldNames(fieldNames);
  const expendKeySet = new Set(expandedKeys);
  const flattenNodeList: InternalFlattenNode[] = [];

  function recursionTreeData(
    treeData: YmDataNode[],
    parent?: InternalFlattenNode,
  ): InternalFlattenNode[] {
    return treeData.map((dataItem, index) => {
      //顶层开始从上往下找
      const currTreeData = dataItem;
      const pos = `${parent ? parent.pos : '0'}-${index}`;
      const currTreeDataChildren = currTreeData[currFieldNames.children];
      const currFlattenTreeData: InternalFlattenNode = {
        key: currTreeData[currFieldNames.key],
        title: currTreeData[currFieldNames.title],
        parent,
        pos,
        data: currTreeData,
        isStart: [...(parent ? parent.isStart : []), index === 0],
        isEnd: [...(parent ? parent.isEnd : []), index === treeData.length - 1], //在当前这一层
      };
      flattenNodeList.push(currFlattenTreeData);
      if (expendKeySet.has(currFlattenTreeData.key) && currTreeDataChildren) {
        //expendKeys的children肯定是要渲染的
        currFlattenTreeData.children = recursionTreeData(
          currTreeDataChildren,
          currFlattenTreeData,
        );
      }
      return currFlattenTreeData;
    });
  }

  recursionTreeData(treeData);

  return flattenNodeList;
}

interface TreeNodeRequiredProps {
  expandedKeys: Key[];
  checkedKeys: Key[];
  halfCheckedKeys: Key[];
  loadedKeys: Key[];
  loadingKeys: Key[];
  selectedKeys: Key[];
}

type GetTreeNodePropsEventHandler = (
  key: string,
  treeNodeRequiredProps: TreeNodeRequiredProps,
  keyEntities: Record<Key, InternalDataNodeForDataMap>,
) => {
  expanded: boolean;
  level: number;
  loaded: boolean;
  loading: boolean;
};

export const getTreeNodeProps: GetTreeNodePropsEventHandler = (
  key,
  treeNodeRequiredProps,
  keyEntities,
) => {
  if (key === MOTION_KEY) {
    return {
      expanded: true,
      level: 0,
      loaded: false,
      loading: false,
      checked: false,
      halfChecked: false,
      selected: false,
    };
  }

  const entity = keyEntities[key];
  const {
    expandedKeys,
    loadedKeys,
    loadingKeys,
    checkedKeys,
    halfCheckedKeys,
    selectedKeys,
  } = treeNodeRequiredProps;
  return {
    expanded: expandedKeys.indexOf(key) > -1,
    checked: checkedKeys.indexOf(key) > -1,
    halfChecked: halfCheckedKeys.indexOf(key) > -1,
    loading: loadingKeys.indexOf(key) > -1,
    loaded: loadedKeys.indexOf(key) > -1,
    selected: selectedKeys.indexOf(key) > -1,
    level: entity.level,
  };
};

export function getDragChildrenKeys(
  dragNodeKey: Key,
  keyEntities: Record<string, InternalDataNodeForDataMap>,
): string[] {
  const dragChildrenKeys: string[] = [];

  const entity = keyEntities[dragNodeKey];
  function dig(list: InternalDataNodeForDataMap[] = []) {
    list.forEach(({ key, children }) => {
      dragChildrenKeys.push(key);
      dig(children);
    });
  }

  dig(entity.children);

  return dragChildrenKeys;
}

// disable默认false,showCheckableBox:默认true,为true时要判断disableCheckbox
function _isCheckDisabled(node: YmDataNode): boolean {
  const { showCheckableBox, disableCheckbox, disabled } = node;
  if (disabled) {
    return true;
  } else if (showCheckableBox === false) {
    return true;
  } else {
    return !!disableCheckbox;
  }
}

// 某个key不可能同时存在 checkedKeys 和halfCheckedKeys 中
function _removeFromCheckedKeys(
  halfCheckedKeys: Set<Key>,
  checkedKeys: Set<Key>,
) {
  const filteredKeys = new Set<Key>();
  halfCheckedKeys.forEach((key) => {
    if (!checkedKeys.has(key)) {
      filteredKeys.add(key);
    }
  });
  return filteredKeys;
}

/**
 *
 *
 * @export
 * @param {Key[]} originCheckedKeys
 * @param {Record<Key,InternalDataNodeForDataMap>} keyEntities
 * @param {boolean | undefined} noNeedUpToDown
 * @returns {{checkedKeys:Key[],halfCheckedKeys:Key[]}}
 * @description 从未选中--选中时 根据checkedKeys 设置TreeNode 的 选中状态/半选状态
 */
export function fillConductCheck(
  originCheckedKeys: Key[],
  keyEntities: Record<Key, InternalDataNodeForDataMap>,
): { checkedKeys: Key[]; halfCheckedKeys: Key[] } {
  const allTreeDataKeys: Key[] = [];
  let maxLevel = 0;
  // 把 keyEntities 从key 为 key的对象转换成key为level的对象
  const levelEntities = new Map<number, Set<InternalDataNodeForDataMap>>();
  Object.keys(keyEntities).forEach((key) => {
    allTreeDataKeys.push(key);
    const entity = keyEntities[key];
    const { level } = entity;
    let levelEntitySet: Set<InternalDataNodeForDataMap> | undefined =
      levelEntities.get(level);
    if (!levelEntitySet) {
      levelEntitySet = new Set();
      levelEntities.set(level, levelEntitySet);
    }
    levelEntitySet.add(entity);
    maxLevel = Math.max(maxLevel, level);
  });

  const checkedKeySet = new Set(
    originCheckedKeys.filter((orgKey) => allTreeDataKeys.indexOf(orgKey) > -1),
  );

  //从上到下
  for (let i = 0; i <= maxLevel; i++) {
    const currLevelEntitySet = levelEntities.get(i) || new Set();
    currLevelEntitySet.forEach((entity) => {
      const { node, key, children } = entity;
      if (
        checkedKeySet.has(key) &&
        !_isCheckDisabled(node) &&
        children &&
        children.length
      ) {
        children
          .filter(
            (childFilterEntity) => !_isCheckDisabled(childFilterEntity.node),
          )
          .forEach((childEachEntity) => {
            checkedKeySet.add(childEachEntity.key);
          });
      }
    });
  }

  //从下到上
  const halfCheckedKeys = new Set<Key>();
  const visitedKeys = new Set<Key>();
  for (let i = maxLevel; i >= 0; i--) {
    const currLevelEntitySet = levelEntities.get(i) || new Set();
    currLevelEntitySet.forEach((entity) => {
      let allChecked = true;
      let partialChecked = false;
      const { node, parent } = entity;
      if (_isCheckDisabled(node) || !parent || visitedKeys.has(parent?.key)) {
        return;
      }

      if (_isCheckDisabled(parent.node)) {
        visitedKeys.add(parent.key);
        return;
      }
      (parent.children || [])
        .filter(
          (childFilterEntity) => !_isCheckDisabled(childFilterEntity.node),
        )
        .forEach((childEachEntity) => {
          if (allChecked && !checkedKeySet.has(childEachEntity.key)) {
            //只要有一个没有
            allChecked = false;
          }
          if (!partialChecked && checkedKeySet.has(childEachEntity.key)) {
            //只要有一个有
            partialChecked = true;
          }
        });
      if (allChecked) {
        checkedKeySet.add(parent.key);
      } else if (partialChecked) {
        halfCheckedKeys.add(parent.key);
      }
      visitedKeys.add(parent.key);
    });
  }

  return {
    checkedKeys: Array.from(checkedKeySet),
    halfCheckedKeys: Array.from(
      _removeFromCheckedKeys(halfCheckedKeys, checkedKeySet),
    ),
  };
}

export function cleanConductCheck(
  operationKey: string,
  originCheckedKeys: Key[],
  originHalfCheckedKeys: Key[],
  keyEntities: Record<Key, InternalDataNodeForDataMap>,
): { checkedKeys: Key[]; halfCheckedKeys: Key[] } {
  //1.找出当前操作的checkbox key所有的后代key,并从originCheckedKeys、originHalfCheckedKeys中删除
  const operationEntity = keyEntities[operationKey];
  const laterKeySet = new Set<Key>();
  function _recursionGetLaterKeys(
    entity: InternalDataNodeForDataMap,
    laterKeySet: Set<Key>,
  ) {
    (entity.children ?? []).forEach((childEntity) => {
      if (!_isCheckDisabled(childEntity.node)) {
        laterKeySet.add(childEntity.key);
      }
      _recursionGetLaterKeys(childEntity, laterKeySet);
    });
  }
  _recursionGetLaterKeys(operationEntity, laterKeySet);

  const currCheckedKeys = originCheckedKeys.reduce(
    (prev: Key[], currKey: Key, index: number, array: Key[]) => {
      if (!laterKeySet.has(currKey)) {
        prev.push(currKey);
      }
      return prev;
    },
    [],
  );

  const currHalfCheckedKeys = originHalfCheckedKeys.reduce(
    (prev: Key[], currKey: Key, index: number, array: Key[]) => {
      if (!laterKeySet.has(currKey)) {
        prev.push(currKey);
      }
      return prev;
    },
    [],
  );

  // 2.把 keyEntities 从key 为 key的对象转换成key为level的对象
  const allTreeDataKeys: Key[] = [];
  const levelEntities = new Map<number, Set<InternalDataNodeForDataMap>>();
  Object.keys(keyEntities).forEach((key) => {
    allTreeDataKeys.push(key);
    const entity = keyEntities[key];
    const { level } = entity;
    let levelEntitySet: Set<InternalDataNodeForDataMap> | undefined =
      levelEntities.get(level);
    if (!levelEntitySet) {
      levelEntitySet = new Set();
      levelEntities.set(level, levelEntitySet);
    }
    levelEntitySet.add(entity);
  });

  const checkedKeySet = new Set(
    currCheckedKeys.filter((orgKey) => allTreeDataKeys.indexOf(orgKey) > -1),
  );
  const halfCheckedKeySet = new Set(
    currHalfCheckedKeys.filter(
      (orgKey) => allTreeDataKeys.indexOf(orgKey) > -1,
    ),
  );

  // 3.从当前操作的node的level往上
  const maxLevel = operationEntity.level;
  const visitedKeys = new Set<Key>();
  for (let i = maxLevel; i >= 0; i--) {
    const currLevelEntitySet = levelEntities.get(i) || new Set();
    currLevelEntitySet.forEach((entity) => {
      let allChecked = true;
      let partialChecked = false;
      const { node, parent } = entity;
      if (_isCheckDisabled(node) || !parent || visitedKeys.has(parent?.key)) {
        return;
      }

      if (_isCheckDisabled(parent.node)) {
        visitedKeys.add(parent.key);
        return;
      }
      (parent.children || [])
        .filter(
          (childFilterEntity) => !_isCheckDisabled(childFilterEntity.node),
        )
        .forEach((childEachEntity) => {
          if (allChecked && !checkedKeySet.has(childEachEntity.key)) {
            //只要一个没有
            allChecked = false;
          }
          if (!partialChecked && checkedKeySet.has(childEachEntity.key)) {
            //只要一个有
            partialChecked = true;
          }
        });
      if (!allChecked) {
        checkedKeySet.delete(parent.key);
        if (!partialChecked) {
          halfCheckedKeySet.delete(parent.key);
        }
      }
      visitedKeys.add(parent.key);
    });
  }

  return {
    checkedKeys: Array.from(checkedKeySet),
    halfCheckedKeys: Array.from(
      _removeFromCheckedKeys(halfCheckedKeySet, checkedKeySet),
    ),
  };
}
